/*
 * This file is part of Kubriko.
 * Copyright (c) Pandula Péter 2025.
 * https://github.com/pandulapeter/kubriko
 *
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
 * If a copy of the MPL was not distributed with this file, You can obtain one at
 * https://mozilla.org/MPL/2.0/.
 */
package com.gitee.wsl.common.ui.viewport.scene

import kotlin.jvm.JvmInline
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min

/**
 * Wrapper for 1D dimensions in the context of a Scene.
 * Regular [Float] values should be used for screen dimensions while this wrapper should be used for Scene dimensions.
 *
 * Use the [sceneUnit] extension property for casting.
 */
@JvmInline
value class SceneUnit internal constructor(val raw: Float) : Comparable<SceneUnit> {

    operator fun plus(other: SceneUnit): SceneUnit = (raw + other.raw).sceneUnit

    operator fun unaryPlus(): SceneUnit = raw.unaryPlus().sceneUnit

    operator fun minus(other: SceneUnit): SceneUnit = (raw - other.raw).sceneUnit

    operator fun unaryMinus(): SceneUnit = raw.unaryMinus().sceneUnit

    operator fun times(scale: Float): SceneUnit = (raw * scale).sceneUnit

    operator fun times(sceneUnit: SceneUnit): SceneUnit = (raw * sceneUnit.raw).sceneUnit

    operator fun times(scale: Int): SceneUnit = (raw * scale.toFloat()).sceneUnit

    operator fun div(scale: Float): SceneUnit = (raw / scale).sceneUnit

    operator fun div(scale: SceneUnit): SceneUnit = (raw / scale.raw).sceneUnit

    operator fun div(scale: Int): SceneUnit = (raw / scale.toFloat()).sceneUnit

    override operator fun compareTo(other: SceneUnit) = raw.compareTo(other.raw)

    operator fun rangeTo(other: SceneUnit) = raw.rangeTo(other.raw)

    operator fun rangeUntil(other: SceneUnit) = raw.rangeUntil(other.raw)

    override fun toString(): String = "SceneUnit($raw)"

    companion object {
        val Zero = SceneUnit(0f)
        val Unit = SceneUnit(1f)
    }
}

val Float.sceneUnit: SceneUnit
    get() = SceneUnit(this)

operator fun Float.times(scale: SceneUnit): SceneUnit = (scale.raw * this).sceneUnit

val Int.sceneUnit: SceneUnit
    get() = SceneUnit(toFloat())

operator fun Int.times(scale: SceneUnit): SceneUnit = (scale.raw * this).sceneUnit

val Long.sceneUnit: SceneUnit
    get() = SceneUnit(toFloat())

operator fun Long.times(scale: SceneUnit): SceneUnit = (scale.raw * this).sceneUnit


fun SceneUnit.clamp(
    min: SceneUnit? = null,
    max: SceneUnit? = null,
) = max(min?.raw ?: raw, min(max?.raw ?: raw, raw)).sceneUnit

val SceneUnit.abs get() = abs(raw).sceneUnit

fun abs(sceneUnit: SceneUnit) = sceneUnit.abs

fun min(a: SceneUnit, b: SceneUnit) = min(a.raw, b.raw).sceneUnit

fun max(a: SceneUnit, b: SceneUnit) = max(a.raw, b.raw).sceneUnit